package com.hex.ds.hdrs.engine.manager;

import cn.hutool.core.util.StrUtil;
import com.hex.ds.hdrs.app.service.AppManager;
import com.hex.ds.hdrs.common.constant.Const;
import com.hex.ds.hdrs.common.constant.ErrorCode;
import com.hex.ds.hdrs.common.exception.BizException;
import com.hex.ds.hdrs.common.sort.SortQueue;
import com.hex.ds.hdrs.common.utils.HdrsDateUtil;
import com.hex.ds.hdrs.period.job.po.JobTask;
import com.hex.ds.hdrs.period.task.po.PeriodTask;
import com.hex.ds.hdrs.period.task.po.TaskTrack;
import com.hex.ds.hdrs.period.task.util.TaskTrackHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Package: com.hex.ds.hdrs.engine.manager
 * @ClassName ToRunManager
 * @Description: 待处理管理
 * @Author: jd.chen
 * @Date 2021/7/5 17:55
 * @Version v1.0
 **/
@Slf4j
@Component
public class ToRunManager implements ApplicationContextAware {

    private static ApplicationContext context;

    /**
     * toRunJobList 同步锁
     */
    private final byte[] listSyn = {};
    /**
     * toRunJobMap 同步锁
     */
    private final byte[] jobMapSyn = {};

    @Value("${dispatcher.sleep}")
    private int dispatcherSleep;
    @Value("${workFail.delay}")
    private int workFailDelay;

    @Autowired
    private AppManager appManager;

    //可执行作业list
    private volatile List<JobTask> toRunJobList = Collections.synchronizedList(new ArrayList<>());
    //可执行作业Map
    private volatile Map<String, JobTask> toRunJobMap = new ConcurrentHashMap<>();
    //应用 作业list  Map<appCode,SortQueue>
    private volatile Map<String, SortQueue> appToRunMap = new ConcurrentHashMap<>();

    /**
     * @Method addJobTaskList
     * @param jobTask
     * @return boolean
     * @Description 添加jobTask
     * @Author jian.qiao
     * @Date 2022/11/23 18:54
     * @Version V1.0
     */
    public boolean addJobTaskList(JobTask jobTask){
        boolean flag = false;
        if (!toRunJobList.contains(jobTask)){
            flag = this.toRunJobList.add(jobTask);
        }
        return flag;
    }
    /**
     * 清空内存
     */
    public void clear() {
        synchronized (this){
            toRunJobList.clear();
            toRunJobMap.clear();
            appToRunMap.clear();
        }
    }

    /**
     * 添加作业
     */
    public void addJob(JobTask job, PeriodTask task, String type) {
        // 任务轨迹
        try {
            log.debug("=====》【toRun addJob】《=====作业【{}】【{}】任务【{}】【{}】", job.getJobCode(), job.getBizDate(), task.getTaskCode(), task.getBizDate());
            if (this.isContainsJob(job.getJobCode())) {
                log.warn("【toRun addJob】 任务已存在，不添加===【{}】【{}】任务【{}】【{}】", job.getJobCode(), job.getBizDate(), task.getTaskCode(), task.getBizDate());
                return;
            }
            TaskTrackHelper.addTaskTrack(task, TaskTrackHelper.processorSucc(type));
            synchronized (listSyn) {
                if (!addJobTaskList(job)) {
                    log.error("类型【{}】toRun新增作业失败，作业【{}】、任务【{}】、日期【{}】", type, job.getJobCode(), task.getTaskCode(), task.getBizDate());
                }
                toRunJobMap.put(job.getJobCode(), job);
                this.addAppToRunMap(job, task);
            }
        } catch (Exception e) {
            log.error("【toRun addJob】异常: {}", e.getMessage(), e);
        }
        if (!isContainsJob(job.getJobCode())) {
            log.error("类型【{}】toRun新增作业异常，内存数据不一致。作业【{}】、任务【{}】、日期【{}】", type, job.getJobCode(), task.getTaskCode(), task.getBizDate());
            removeJob(task,"toRun新增作业异常，内存数据不一致");
            throw new BizException(ErrorCode.ERROR_821005);
        }
    }

    /**
     * 任务业务失败时添加作业
     */
    public void addWorkFailJob(JobTask job, PeriodTask task, String type) {
        // 任务轨迹
        try {
            log.debug("=====》【toRun addJob】《=====作业【{}】【{}】任务【{}】【{}】", job.getJobCode(), job.getBizDate(), task.getTaskCode(), task.getBizDate());
            if (this.isContainsJob(job.getJobCode())) {
                log.debug("【toRun addJob】 任务已存在，不添加===【{}】【{}】任务【{}】【{}】", job.getJobCode(), job.getBizDate(), task.getTaskCode(), task.getBizDate());
                return;
            }

            TaskTrackHelper.addTaskTrack(task, TaskTrackHelper.processorSucc(type));

            synchronized (listSyn) {
                List<JobTask> jobList = new ArrayList<>();
                jobList.add(job);
                boolean removeFlag = toRunJobList.addAll(jobList);
                if (!removeFlag) {
                    log.error("类型【{}】toRun新增作业失败，作业【{}】、任务【{}】、日期【{}】", type, job.getJobCode(), task.getTaskCode(), task.getBizDate());
                }
                toRunJobMap.put(job.getJobCode(), job);
                this.addWorkFailAppToRunMap(job, task);
            }
        } catch (Exception e) {
            log.error("【toRun addJob】异常: {}", e.getMessage(), e);
        }

        //this.checkAddJobTask(job, task);
    }

    /**
     * 校验数据一致性
     */
    public boolean checkAddJobTask(String jobCode) {
        if (this.getJob(jobCode) == null){
            return false;
        }
        JobTask job = this.getJob(jobCode);
        if (!toRunJobList.contains(job)) {
            return false;
        }
        SortQueue sortQueue = this.appToRunMap.get(job.getAppCode());
        boolean flag = false;
        for (PeriodTask task : job.getPeriodTasks()) {
            if (sortQueue.contain(task)) {
                flag = true;
            }
        }
        return flag;
    }

    /**
     * 处理任务异常
     *
     * @param task
     * @param error
     */
    public void taskDealFail(PeriodTask task, String error) {
        if (null != task) {
            TaskTrack taskTrack = TaskTrackHelper.processorFail(error);
            TaskTrackHelper.addTaskTrack(task, taskTrack);
        }
    }

    /**
     * 添加作业
     */
    private void addAppToRunMap(JobTask job, PeriodTask task) {
        log.info("=====》【toRun addAppToRunMap】《=====作业【{}】、任务【{}】、日期【{}】", job.getJobCode(), task.getTaskCode(), job.getBizDate());
        String appCode = job.getAppCode();
        if (!this.isContainsApp(appCode)) {
            log.error("【addAppToRunMap】异常:该应用【{}】queue不存在", appCode);
        } else {
            this.getSortQueue(appCode).add(task);
        }
    }

    /**
     * 任务业务失败添加任务
     */
    private void addWorkFailAppToRunMap(JobTask job, PeriodTask task) {
        log.info("=====》【toRun addAppToRunMap】《=====作业【{}】、任务【{}】、日期【{}】", job.getJobCode(), task.getTaskCode(), job.getBizDate());
        String appCode = job.getAppCode();
        if (!this.isContainsApp(appCode)) {
            log.error("【addWorkFailAppToRunMap】异常:该应用【{}】queue不存在", appCode);
        } else {
            this.getSortQueue(appCode).addToDelay(task, workFailDelay);
        }
    }

    /**
     * 移除作业
     */
    public void removeJob(PeriodTask task, String type) {
        try {
            if (task == null) {
                log.error("【可执行队列】删除作业及任务实例失败：任务实例为空!, 类型: {}", type);
                return;
            }
            log.debug("【可执行队列】删除作业作业编码: {}、当前任务编码: {}、业务日期: {}, 类型: {}", task.getJobCode(), task.getTaskCode(), task.getBizDate(), type);
            String jobCode = task.getJobCode();
            JobTask job = this.getJob(jobCode);

            synchronized (listSyn) {
                this.toRunJobList.remove(job);
                this.toRunJobMap.remove(jobCode);
                SortQueue sortQueue = getSortQueue(task.getAppCode());
                sortQueue.remove(task);
            }

            //this.checkDelJobTask(job, task, sortQueue);
        } catch (Exception e) {
            log.error("【可执行队列】删除作业作业编码: {}、当前任务编码: {}、业务日期: {}, 类型: {}, 异常：{}", task.getJobCode(), task.getTaskCode(), task.getBizDate(), type, e.getMessage(), e);
        }
    }

    /**
     * 判断是否一致
     *
     * @param job
     * @param task
     */
    public void checkDelJobTask(JobTask job, PeriodTask task, SortQueue sortQueue) {
        try {
            String jobCode = task.getJobCode();
            String bizDate = task.getBizDate();
            if (toRunJobList.contains(job)) {
                log.error("作业【{}】、日期【{}】【checkDelJobTask】toRunList删除失败", jobCode, bizDate);
            } else if (this.isContainsJob(jobCode)) {
                log.error("作业【{}】、日期【{}】【checkDelJobTask】toRunMap删除失败", jobCode, bizDate);
            } else if (sortQueue.contain(task)) {
                log.error("作业【{}】、日期【{}】【checkDelJobTask】sortQueue删除失败", jobCode, bizDate);
            }
        } catch (Exception e) {
            log.error("【checkDelJobTask】异常:{}", e.getMessage());

        }
    }

    /**
     * 处理任务
     *
     * @param groupCode
     * @return
     */
    public PeriodTask dealJobTask(String groupCode) {
        try {
            String appCode = appManager.getFitAppForDispatcher(groupCode);
            if (StrUtil.isEmpty(appCode)){
                return null;
            }
            SortQueue sortQueue = this.getSortQueue(appCode);
            if (null != sortQueue && !sortQueue.isEmpty()) {
                return sortQueue.pop();
            }
            return null;
        } catch (BizException e) {
            log.error("【dealJobTask】biz异常:{}", e.getErrMsg(), e);
            return null;
        } catch (Exception e) {
            log.error("【dealJobTask】异常:{}", e.getMessage(),e);
            return null;
        }
    }

    /**
     * 可执行队列添加到待处理队列
     *
     * @param task
     * @param type
     * @param toDoManager
     */
    public void toRunToToDo(PeriodTask task, String type, ToDoManager toDoManager) {
        log.info("=====》【toRunToToDo】《=====作业【{}】、任务【{}】、日期【{}】", task.getJobCode(), task.getTaskCode(), task.getBizDate());
        JobTask job = getJob(task.getJobCode());
        toDoManager.addJob(job, type);
        this.removeJob(task, "Run_To_Do");
    }

    /**
     * 可执行队列添加到正在执行队列
     *
     * @param task
     * @param runningManager
     */
    public void toRunToRunning(PeriodTask task, String type, RunningManager runningManager) {
        log.info("=====》【可执行队列转移到正在执行队列】《=====作业【{}】、任务【{}】、日期【{}】", task.getJobCode(), task.getTaskCode(), task.getBizDate());
        JobTask job = getJob(task.getJobCode());
        task.setTaskStatus(Const.TASK_RUNNING);
        task.setUpTs(HdrsDateUtil.getCurTs());
        runningManager.addJob(job, task, type);
        this.removeJob(task, "Run_To_Running");
    }

    /**
     * 休眠
     *
     * @throws InterruptedException
     */
    public void threadSleep() {
        try {
            Thread.sleep(dispatcherSleep);
        } catch (InterruptedException e) {
            log.error("【threadSleep】异常:{}", e.getMessage());
            Thread.currentThread().interrupt();
        }
    }

    /**
     * 将任务移动到队首
     *
     * @param task
     */
    public void taskToTop(PeriodTask task) {
        SortQueue sortQueue = getSortQueue(task.getAppCode());
        sortQueue.moveToFist(task);
    }

    /**
     * 任务升序
     *
     * @param task
     */
    public void taskUpSort(PeriodTask task, int num) {
        SortQueue sortQueue = getSortQueue(task.getAppCode());
        sortQueue.upSort(task, num);
    }

    /**
     * 任务降序
     *
     * @param task
     */
    public void taskDownSort(PeriodTask task, int num) {
        SortQueue sortQueue = getSortQueue(task.getAppCode());
        sortQueue.downSort(task, num);
    }

    public void addSortQueue(String appCode, SortQueue sortQueue) {
        appToRunMap.put(appCode, sortQueue);
    }

    public JobTask getJob(String jobCode) {
        synchronized (jobMapSyn) {
            return toRunJobMap.get(jobCode);
        }
    }

    public SortQueue getSortQueue(String appCode) {
        synchronized (appToRunMap) {
            if (appCode == null){
                log.error("groupId为空【ToRunManager.getSortQueue】");
                return null;
            }
            return appToRunMap.get(appCode);
        }
    }

    public List<JobTask> getToRunJobList() {
        return toRunJobList;
    }

    public void setToRunJobList(List<JobTask> toRunJobList) {
        this.toRunJobList = toRunJobList;
    }

    public Map<String, JobTask> getToRunJobMap() {
        return toRunJobMap;
    }

    public void setToRunJobMap(Map<String, JobTask> toRunJobMap) {
        this.toRunJobMap = toRunJobMap;
    }

    public Map<String, SortQueue> getAppToRunMap() {
        return appToRunMap;
    }

    public void setAppToRunMap(Map<String, SortQueue> appToRunMap) {
        this.appToRunMap = appToRunMap;
    }

    public boolean isContainsJob(String jobCode) {
        return checkAddJobTask(jobCode);
    }

    public boolean isContainsApp(String appCode) {
        return this.getSortQueue(appCode) == null ? false : true;
    }

    public void addTaskToRunQueue(JobTask jobTask, PeriodTask downTask) {
        this.addAppToRunMap(jobTask, downTask);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    public static ToRunManager getInstance() {
        return context.getBean(ToRunManager.class);
    }
}
